package com.example.demo.client;

import java.net.URLEncoder;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import com.example.demo.auth.AuthenticationKit;
import com.example.demo.entity.ClientToken;
import com.jfinal.qyweixin.sdk.api.ApiResult;

public class BaseServerApi {
	private final static Logger log = LoggerFactory.getLogger(BaseServerApi.class);

	@Autowired
	ClientTokenService clientTokenService;

	/**
	 * 检查是否重写方法
	 */
	private void validOverriideMethod() {
		if (StringUtils.isBlank(getApiHost())) {
			throw new IllegalArgumentException("请检查是否重写了 [" + getClass() + "]这个类的 getApiHost()方法以及方法正确被调用！");
			
		}
		if (StringUtils.isBlank(getAppid())) {
			throw new IllegalArgumentException("请检查是否重写了 [" + getClass() + "]这个类的 getAppid()方法以及方法正确被调用！");
			
		}
		if (StringUtils.isBlank(getAppsecret())) {
			throw new IllegalArgumentException("token获取失败！请检查是否重写了 [" + getClass() + "]这个类的 getAppsecret()方法以及方法正确被调用！");
			
		}
	}

	protected String getApiHost() {
		return null;
	}

	protected String getAppid() {
		return null;
	}

	protected String getAppsecret() {
		return null;
	}
	
	/**
	 * 
	 * @param url 接口路径
	 * @param apiHost 服务地址http://localhost:port/projname
	 * @param appid 产品id
	 * @param secret 调用token接口时为appsercet 调用其他api接口为 token值
	 * @param params 请求参数 （调用此方法，代表参数不需要序列化）
	 * @return
	 */
	public String packageSignUrlEncodeWithPostParams(String url, String apiHost, String appid, 
			String secret, String params) {
		return packageSignUrlEncodeWithPostParams(url, apiHost, appid, secret, params, false);
	}

	/**
	 * post请求 参数参与签名
	 * 
	 * @param url 接口路径
	 * @param apiHost 服务地址http://localhost:port/projname
	 * @param appid 产品id
	 * @param secret 调用token接口时为appsercet 调用其他api接口为 token值
	 * @param params 请求参数 
	 * @param ecode 请求参数是否序列化 ，true 需要序列化，false不需要序列化
	 * @return
	 */
	public String packageSignUrlEncodeWithPostParams(String url, String apiHost, String appid, 
			String secret, String params, boolean encode) {
		log.info("----------------------签名 开始------------------------");
		try {
			String signUrl = AuthenticationKit.getSignUrl(url, appid);
			log.info("无参待签名路径：" + signUrl);
			
			if(encode) {//序列化参数
				params = URLEncoder.encode(params, AuthenticationKit.utf8);
			} 
			String signUrlWithParams = signUrl + params;
			log.info("待参待签名路径：" + signUrlWithParams);
			
			String tokenSign = AuthenticationKit.signUrlEncode(signUrlWithParams, secret);
			log.info("签名：" + tokenSign);
			
			String signUrlEncode = apiHost + signUrl + "&sign=" + URLEncoder.encode(tokenSign, "UTF-8");
			log.info("签名路径：" + signUrlEncode);
			log.info("----------------------签名 end------------------------");
			
			return signUrlEncode;
		} catch (Exception e) {
			e.printStackTrace();
			log.info("----------------------签名 error------------------------");
		}
		return null;
	}
	
	/**
	 * 
	 * @param url 接口路径
	 * @param secret 调用token接口时为appsercet 调用其他api接口为 token值
	 * @param params 请求参数
	 * @return
	 */
	public String packageSignUrlEncodeWithPostParams(String url, String secret, String params) {
		validOverriideMethod();
		return packageSignUrlEncodeWithPostParams(url, getApiHost(), getAppid(), secret, params);
	}

	/**
	 * get请求参数 参与签名
	 * 
	 * @param url 接口路径
	 * @param apiHost 服务地址http://localhost:port/projname
	 * @param appid 产品id
	 * @param secret 调用token接口时为appsercet 调用其他api接口为 token值
	 * @param params 请求参数
	 * @return
	 */
	public String packageSignUrlEncodeWithGetParams(String url, String apiHost, String appid, 
			String secret, Map<String, String> params) {
		log.info("----------------------签名 开始------------------------");
		try {
			String signUrl = AuthenticationKit.getSignUrl(url, appid, params);
			log.info("待参待签名路径：" + signUrl);
			
			String tokenSign = AuthenticationKit.signUrlEncode(signUrl, secret);
			log.info("签名：" + tokenSign);
			
			String signUrlEncode = apiHost + signUrl + "&sign=" + URLEncoder.encode(tokenSign, "UTF-8");
			log.info("签名路径：" + signUrlEncode);
			log.info("----------------------签名 结束------------------------");
			return signUrlEncode;
		} catch (Exception e) {
			e.printStackTrace();
			log.info("----------------------签名 出错------------------------");
		}
		
		return null;
	}
	
	/**
	 * 
	 * @param url 接口路径
	 * @param secret 调用token接口时为appsercet 调用其他api接口为 token值
	 * @param params 请求参数
	 * @return
	 */
	public String packageSignUrlEncodeWithGetParams(String url,  String secret, Map<String, String> params) {
		validOverriideMethod();
		return packageSignUrlEncodeWithGetParams(url, getApiHost(), getAppid(), secret, params);
	}
	
	/**
	 * 
	 * @param url 接口路劲
	 * @param secret 调用token接口时为appsercet 调用其他api接口为 token值
	 * @return
	 */
	public String packageSignUrlEncode(String url, String secret) {
		validOverriideMethod();
		return packageSignUrlEncode(url, getApiHost(), getAppid(), secret);
	}

	/**
	 * 组装加密签名路径
	 *
	 * @param url
	 * @param appid
	 * @param appSecret
	 * @return
	 * @throws Exception
	 */
	public String packageSignUrlEncode(String url, String apiHost, String appid, String appSecret) {
		log.info("----------------------签名 开始------------------------");
		try {
			String signUrl = AuthenticationKit.getSignUrl(url, appid);
			log.info("待签名路径：" + signUrl);
			
			String tokenSign = AuthenticationKit.signUrlEncode(signUrl, appSecret);
			log.info("签名：" + tokenSign);
			
			String signUrlEncode = apiHost + signUrl + "&sign=" + URLEncoder.encode(tokenSign, "UTF-8");
			log.info("签名+路径：" + signUrlEncode);
			
			log.info("----------------------签名 end------------------------");
			return signUrlEncode;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}


	/**
	 * 获取投诉系统token
	 * @return
	 */
	public String getToken(String url) {
		validOverriideMethod();
		return getToken(url, getApiHost(), getAppid(), getAppsecret());
	}
	
	protected String getToken() {
		return null;
	}
	
	/**
	 * 获取投诉系统token
	 * @param appid
	 * @param appsecret
	 * @return
	 */
	public String getToken(String url, String apiHost, String appid, String appsecret) {
		log.info("----------------------获取token 开始------------------------");
		ClientToken token = clientTokenService.findByAppId(appid);
		// 判断token 是否过期
		long time = System.currentTimeMillis();
		if (token != null && token.getExpire() != null && token.getExpire() > time / 1000) {
			log.info("----------------------获取token未过期["+token.getToken()+"]["+token.getExpire()+"] end------------------------");
			return token.getToken();
		} else {
			String tokenRet = token(url, apiHost, appid, appsecret);
			if (tokenRet == null || "".equals(tokenRet)) {
				throw new IllegalArgumentException("token获取失败！请检查是否重写了 [" + getClass() + "]这个类的 token()方法以及方法正确被调用！");
			}
			return tokenRet;
		}

	}

	/**
	 * 需要重写
	 * 
	 * @return
	 */
	private String token(String url, String apiHost, String appid, String appsecret) {
		log.info("----------------------获取新token 开始------------------------");
		String urlSignEncode = packageSignUrlEncode(url, apiHost, appid, appsecret);

		String jsonStr = HttpHttpsUtil.httpUrlGet(urlSignEncode);

		ApiResult apiResult = new ApiResult(jsonStr);
		Map map = apiResult.getMap("data");
		String tokenStr = (String)map.get("token");
		Number expire = (Number)map.get("expire");
		// 保存新获取的token
		clientTokenService.modifyToken(appid, tokenStr, expire.longValue());
		log.info("----------------------获取新token [tokenStr][expire] end------------------------");
		return tokenStr;
	}
	
}
